package com.github.authcloud.client.configure;

import com.github.authcloud.common.AuthConstant;
import com.github.authcloud.common.RequiresRolePermissionMapping;
import com.github.authcloud.common.NoRequiresLoginMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : zhaoxuan
 * @version : 1.0
 * @date : 2021/8/19 21:22
 * @description : AuthClientStarter
 */
public class AuthClientCommandLineRunner implements CommandLineRunner {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private String serviceName;

    private ApplicationContext applicationContext;

    private RedisTemplate<String, String> authRedisTemplate;

    public AuthClientCommandLineRunner(String serviceName, ApplicationContext applicationContext, RedisTemplate authRedisTemplate) {
        this.serviceName = serviceName.toUpperCase();
        this.applicationContext = applicationContext;
        this.authRedisTemplate = authRedisTemplate;
    }

    @Override
    public void run(String... args) throws Exception {
        logger.info("AuthCloud--obtain and upload no login and role and permission mapping info -- start");
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
        List<RequiresRolePermissionMapping> requiresRolePermissionMappingList = new ArrayList<>(500);
        List<NoRequiresLoginMapping> noRequiresLoginUrlList = new ArrayList<>(500);
        for (RequestMappingInfo info : map.keySet()) {
            HandlerMethod handlerMethod = map.get(info);
            Class<? extends HandlerMethod> clazz = handlerMethod.getClass();
            Method method = handlerMethod.getMethod();
            if (!method.isAnnotationPresent(RequiresPermissions.class)
                    && !method.isAnnotationPresent(RequiresRoles.class)
                    && !method.isAnnotationPresent(NoRequiresLogin.class)
                    && !clazz.isAnnotationPresent(NoRequiresLogin.class)
                    && !clazz.isAnnotationPresent(RequiresRoles.class)
                    && !clazz.isAnnotationPresent(RequiresPermissions.class)) {
                continue;
            }
            Set<String> urls = info.getPatternsCondition().getPatterns().stream().map(url -> url.contains("{") && url.contains("}") ? url = url.replaceAll("\\{[^}]*\\}", "[1-9][0-9]*") : url).collect(Collectors.toSet());
            Set<String> methods = info.getMethodsCondition().getMethods().stream().map(RequestMethod::name).collect(Collectors.toSet());
            if (method.isAnnotationPresent(NoRequiresLogin.class) || clazz.isAnnotationPresent(NoRequiresLogin.class)) {
                NoRequiresLoginMapping noRequiresLoginMapping = new NoRequiresLoginMapping();
                noRequiresLoginMapping.setUrls(urls);
                noRequiresLoginMapping.setMethods(methods);
                noRequiresLoginUrlList.add(noRequiresLoginMapping);
                continue;
            }
            RequiresRolePermissionMapping requiresRolePermissionMapping = new RequiresRolePermissionMapping();
            requiresRolePermissionMapping.setUrls(urls);
            requiresRolePermissionMapping.setMethods(methods);
            if (method.isAnnotationPresent(RequiresPermissions.class)) {
                RequiresPermissions permissions = method.getAnnotation(RequiresPermissions.class);
                CollectionUtils.mergeArrayIntoCollection(permissions.value(), requiresRolePermissionMapping.getPermissions());
                requiresRolePermissionMapping.setPermissionsLogical(permissions.logical());
            }
            if (method.isAnnotationPresent(RequiresRoles.class)) {
                RequiresRoles roles = method.getAnnotation(RequiresRoles.class);
                CollectionUtils.mergeArrayIntoCollection(roles.value(), requiresRolePermissionMapping.getRoles());
                requiresRolePermissionMapping.setRolesLogical(roles.logical());
            }
            requiresRolePermissionMappingList.add(requiresRolePermissionMapping);
        }
        authRedisTemplate.opsForHash().put(AuthConstant.NO_LOGIN_MAPPING_LISTENER_KEY, serviceName, noRequiresLoginUrlList);
        authRedisTemplate.convertAndSend(AuthConstant.NO_LOGIN_MAPPING_LISTENER_TOPIC, serviceName);
        authRedisTemplate.opsForHash().put(AuthConstant.AUTH_MAPPING_LISTENER_KEY, serviceName, requiresRolePermissionMappingList);
        authRedisTemplate.convertAndSend(AuthConstant.AUTH_MAPPING_LISTENER_TOPIC, serviceName);
        logger.info("AuthCloud--obtain and upload no login and role and permission mapping info -- end");
    }
}